#include <buola/gui/x11/auto.h>

#include <buola/gui/x11/cpixmap.h>
#include <buola/gui/x11/cx11toolkit.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cvisual.h>
#include <buola/gui/x11/cx11drawable.h>
#include <buola/gui/x11/cx11picture.h>
#include <buola/gui/x11/cglxcontext.h>
#include <buola/gl/copenglgc.h>

namespace buola { namespace gui { namespace x11 {

CPixmap::CPixmap()
{
    mDepth=0;
    mVisual=nullptr;
    mDrawableID=0;
    mOwnID=true;
}

CPixmap::~CPixmap()
{
    if(mDrawableID) Destroy();
}

bool CPixmap::Create(TID pID,const CSize_i &pSize,int pDepth)
{
    mDrawableID=pID;
    mOwnID=false;
    mSize=pSize;
    mDepth=pDepth;
    XVisualInfo lInfo;

    if(mSize.Undefined()||!mDepth)
    {
        Window lRoot;
        int lX,lY;
        unsigned int lWidth,lHeight;
        unsigned int lBorderWidth;
        unsigned int lDepth;
        Status lStatus=XGetGeometry(get_default_x11_display()->GetDisplay(),pID,&lRoot,
                &lX,&lY,&lWidth,&lHeight,&lBorderWidth,&lDepth);
        if(!lStatus)
            return false;

        if(!mDepth)
            mDepth=lDepth;
        if(mSize.Undefined())
            mSize=CSize_i(lWidth,lHeight);
    }

    XMatchVisualInfo(get_default_x11_display()->GetDisplay(),get_default_x11_display()->GetScreenNumber(),
            mDepth,TrueColor,&lInfo);
    mVisual=lInfo.visual;

    return true;
}

void CPixmap::Create(const CSize_i &pSize,int pDepth)
{
    if(!pDepth) pDepth=get_default_x11_display()->GetDepth();
    mDrawableID=get_x11_toolkit()->CreatePixmap(pSize,pDepth);
    mDepth=pDepth;
    XVisualInfo lInfo;
    int lResult=XMatchVisualInfo(get_default_x11_display()->GetDisplay(),get_default_x11_display()->GetScreenNumber(),
            mDepth,TrueColor,&lInfo);
    if(lResult)
        mVisual=lInfo.visual;
    else
        mVisual=nullptr;
    mSize=pSize;
}

void CPixmap::Create(const CSize_i &pSize,int pDepth,unsigned char *pData,
                                                int pStride)
{
    Create(pSize,pDepth);

    //TODO:implement it using XInitImage, so that it's not necessary to get
    //ownership of the image data and destroy it, as it is not needed any
    //more
    XImage *lImage=XCreateImage(get_default_x11_display()->GetDisplay(),get_default_x11_display()->GetVisual(),
        pDepth,ZPixmap,0,(char*)pData,pSize.x,pSize.y,8,pStride);

    XGCValues lGCValues;
    GC lGC=XCreateGC(get_default_x11_display()->GetDisplay(),mDrawableID,0,&lGCValues);
    XPutImage(get_default_x11_display()->GetDisplay(),mDrawableID,lGC,lImage,0,0,0,0,
                    pSize.x,pSize.y);
    XDestroyImage(lImage);
    XFreeGC(get_default_x11_display()->GetDisplay(),lGC);
}

void CPixmap::Destroy()
{
    if(!mDrawableID||!mOwnID) return;
    get_x11_toolkit()->FreePixmap(mDrawableID);
    mDrawableID=0;
}

CSize_i CPixmap::SurfaceSize()
{
    return mSize;
}

img::CGC* CPixmap::GetGC(img::EGCCaps pCaps)
{
    ///\todo fix this
    return nullptr;
//    return new gl::COpenGLGC(new CGLXContext(this));
}

/*
unsigned char *CPixmap::GetRawData()
{
    XImage *lImage=XGetImage(get_default_x11_display()->GetDisplay(),mDrawableID,0,0,
            mSize.x,mSize.y,0xffffffff,ZPixmap);

    unsigned char *lData;

    if(mDepth==1)
    {
        lData=(unsigned char*)alloc_mem(((mSize.x+31)/32)*4*mSize.y);
        memcpy(lData,lImage->data,((mSize.y+31)/32)*4*mSize.y);
    }
    else
    {
        lData=(unsigned char*)alloc_mem(mSize.x*mSize.y*4);
        memcpy(lData,lImage->data,mSize.x*mSize.y*4);
    }

    XDestroyImage(lImage);

    return lData;
}
*/

void CPixmap::BitBlt(const CPoint_d &pPoint,const CSize_d &pSize,
                        CSurface *pSrc,const CPoint_d &pSrcOrigin,
                        bool pGraphicsExposures)
{
/*    CPoint_d lDstPoint=pPoint;
    CPoint_d lSrcPoint=pSrcOrigin;

    if(mDepth==24&&pSrc->Depth()==24)
    {
        XGCValues lGCValues;
        lGCValues.graphics_exposures=pGraphicsExposures;
        GC lGC=XCreateGC(get_default_x11_display()->GetDisplay(),mDrawableID,GCGraphicsExposures,
                                &lGCValues);

        XCopyArea(get_default_x11_display()->GetDisplay(),INTERFACE_X11(pSrc)->mID,mDrawableID,lGC,
            (int)lSrcPoint.x,(int)lSrcPoint.y,(int)pSize.x,(int)pSize.y,
            (int)lDstPoint.x,(int)lDstPoint.y);
    }
    else
    {
        CX11Picture::Composite(*(INTERFACE_RENDER(pSrc)),CX11Picture::mNull,
            *(INTERFACE_RENDER(this)),lSrcPoint,lSrcPoint,pPoint,pSize,img::ECompOp::ATOP);
    }
*/
}

/*namespace x11*/ } /*namespace gui*/ }
#if 0
namespace img {

template<>
CSurface *image_surface_factory<PIXMAP_SURFACE_X11>(const img::CPicture &pImage)
{
    int lDepth=gui::get_toolkit()->GetDefaultDepth();
    const CSize_i &lSize=pImage.Size();
    
    unsigned char *lImageData=nullptr;
    
    if(pImage.Format()==img::FORMAT_RGBA32)
    {
        lDepth=32;
    }
    
    if(lDepth==16)
    {
        throw XNotImplemented("16 bit pixmaps are not supported");
    }
    else if(lDepth==24||lDepth==32)
    {
        lImageData=(unsigned char*)alloc_mem(lSize.x*lSize.y*4);
        memcpy(lImageData,pImage.RawBytes(),lSize.x*lSize.y*4);
    }
    else
    {
        throw XInvalid("unknown pixmap depth");
    }
    
    gui::x11::CPixmap *lReturn=new gui::x11::CPixmap;
    lReturn->Create(lSize,lDepth,lImageData);
    return lReturn;
}

/*namespace img*/ }
#endif
/*namespace buola*/ }